面试常聊的几个排序算法,总结整理一下,
堆排序
目前感觉,最有趣也最有挑战的排序
思路:
将数组视为完全二叉树,其父子关系使用下标确定,
root -> left
arr[left] = arr[root * 2 +1] (下标从0开始哈)
构建大根堆, 所有的二叉树都满足
根节点 > ( 左节点 | 右节点 )
将一个数组构建成一个大根堆,具体操作(堆化操作)
假设左右子树都是大根堆,
则选择左右节点较大的,与根节点比较:
若是小于根节点,符合要求,返回
若是大于根节点,则交换根节点和该节点
交换后,该子树需要继续 堆化操作,直至到以下所有子树都符合要求
堆化操作的前提是需要其子树都符合堆的要求,
故,将一个源数组堆化,需要从底部的树开始,从下往上,从右往左,依次讲个所有的子树堆化,最后整个二叉树也就堆化了
堆化后,假设是大根堆,我们求其正序
第一个元素是最大元素,我们选择好该最大元素,与最后一个元素交换,
接着,继续堆化nums[0,n-2],num[n]已经排好序了
重复以上
可以看到, 堆排序是选择排序的优化哈
1 | /** |
快排
思路:
选择一个中轴,将一个数组分为两部分,使得左边比中轴小,右边比中轴大
实际操作中,将第左边界的第一个值设为中轴,迭代访问后面的元素,若是发现比中轴小,则将该元素插入到中轴左侧
针对左右两部分,递归使用以上操作,递归终止在,对应操作的数组只有一个元素时。
时间复杂度,递归深度是log2n故,时间复杂度是 nlog2n 空间复杂度,递归使用了栈空间,故 log2n
1 | object QuickSort { |
append: 避免移动元素的快排,
1 | static void quickSort(int[] arr, int start, int end) { |
归并
思路:
1,拆分
针对数组,以下标中点将数组划分为左右两部分
针对左右两部分,重复以上操作,直至该数组只有一个元素,(递归出口,一个元素是有序的)
2,合并
将拆分的两个部分,两者都是有序数组,合并成一个有序数组
重复以上
关于合并两个有序数组的优化 取巧,若是第一个数组中最大的元素比第二数组中最小的元素还小,可直接拼接
LeetCode中相关的题目,求数组中的逆序对
1 |
|